跳到主要内容

Shell 重定向输入和输出

输入输出重定向一览

重定向命令列表如下:

# 将输出重定向到 file
command > file
# 追加的方式
command >> file

# 将输入重定向到 file
command < file

# 将文件描述符为 n 的文件重定向到 file
n > file
n >> file # 同理

# 将输出文件 m 和 n 合并。
n >& m
# 将输入文件 m 和 n 合并。
n <& m

# 将开始标记 tag 和 结束 tag 之间的内容作为输入
<<tag

输出重定向

重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:

command1 > file1

上面这个命令执行 command1 然后将输出的内容存入 file1。

注意任何 file1 内的已经存在的内容将被新内容替代。

如果要将新内容添加在文件末尾,需要使用 >> 操作符。

这个 >>> 略有区别,尽管两者都是重定向输出,但是前者会先清空文件,然后再写入内容,而后者会将内容追加写入到现有文件的尾部。

实例:执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):

who > users

执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。

可以使用 cat 命令查看文件内容:

$ cat users
_mbsetupuser console Oct 31 17:35
tianqixin console Oct 31 17:35
tianqixin ttys000 Dec 1 11:33

注意,当前一条命令执行失败后,它的返回值(错误信息)不会通过管道符 | 传递给下一个命令,而是传到 /dev/null,传递到这里的数据都是默认被丢弃的

但是可以将这个错误输出重定向到一个文件中

$ errsh > error.txt

输入重定向

与上面的想法, < 则是把文件的内容重定向到终端中(或者上一条命令)

# 将 file1 里面的内容重定向到 command1 里面
$ command1 < file1

# 等价于
$ cat file1 | command1

这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

注意:输出重定向是大于号 > ,输入重定向是小于号 <

输入输出重定向快捷创建文件

也可以同时使用输入输出重定向符,如下

command1 < infile > outfile

同时替换输入和输出,执行 command1,从文件 infile 读取内容,然后将输出写入到 outfile 中。

基于此原理,可以将脚本中的文本重定向到文件,例如在脚本开头生成一条警告信息到自动生成的文件里面

#!/bin/bash

cat<<EOF>log.txt

This is a generated file. Do not edit. Changes will be overwritten.
EOF

# 下面的内容则不会添加到这个文件里面

echo "say hello"

出现在 cat <<EOF>log.txt 与下一个 EOF 行之间的所有文本行都会被当作 stdin 数据。

输出重定向

最基本的重定向将命令的输出发送到一个文件中,bash shell 用大于号(>)来完成这项功能

# 重定向到 test.txt 这个文件
$ date > test.txt
$ ls -l test.txt
$ cat test.txt

output

-rw-r--r-- 1 alsritter alsritter 32 Feb 11 10:51 test.txt
Fri 11 Feb 2022 10:51:51 AM CST

这个 > 是覆盖,如果想追加输入则使用 >>

使用 tag 来输入多行

cat <<tag
这是多行内容
这是多行内容
这是多行内容
tag

输出:

这是多行内容
这是多行内容
...

这个 tag 可以当做注释使用

:<<!
注释内容...
注释内容...
注释内容...
!

也可以用来传递参数,bc 命令能识别输入重定向,所以使用内联输入重定向

#!/bin/bash

var1=10.46
var2=43.67
var3=33.2
var4=71
var5=$(bc << EOF
scale = 4
a1 = ($var1 * $var2)
b1 = ($var3 * $var4)
a1 + b1
EOF
)
echo The final answer for this mess is $var5

输出:

The final answer for this mess is 2813.9882

也可以创建多行字符串

#!/bin/bash

var1=100

USAGE=$(cat <<-END
This is line one. ${var1}
This is line two.
This is line three.
END
)

echo "$USAGE"

输出

    This is line one. 100
This is line two.
This is line three.

参考 Multi-line string with extra space (preserved indentation)

配合文件描述符使用

Linux 中 0 1 2 是一组特殊文件描述符

  • 1 是标准输出(stdout)
  • 2 是标准错误输出(stderr)
  • 0 是标准输入(stdin)

平时我们会把这样使用 1> 两个符号连着一起,它就代表着把标准输出结果重定向到

# 例如这里把标准输出结果重定向到 list.txt 文件里面。
$ ls 1>list.txt

同理

# 这里就是把标准错误输出重定向到 list 文件里面
$ ls 2>list.txt

然后还有一个 & 也很常使用,它就是用于 “取地址”,上面只说了几个特殊的文件描述符,如果需要自定义文件描述符怎么办呢?就是通过这个 & 引用

示例1:

$ exec 6>test
$ echo 'i love linux shell!!!' 1>&6
$ cat test
i love linux shell!!!

首先把文件描述符 6 指向 test 文件。因为不像那些特殊的描述符 0 1 2,所有的输出都有个默认值,当我们想找描述符 6 的时候我们要用 & 来引用它。

其实我们可以把文件描述符想像成一个文件的引用,它可以指向任何一个文件(包括显示器),指向的过程就是我们修改默认位置的过程。而用 & 符号来找到它指向的目标文件,从而向其写入数据。

示例2:

$ exec 3>&1
$ exec 1>test
$ echo "这句话被存到test文件中"
$ echo "还有这句"
$ exec 1>&3
$ echo "这句话输出到显示器"

首先文件描述符 1 默认指向的是显示器,用 & 来找到文件描述符 1 指向的目标文件,也就是显示器。因此文件描述符 3 也指向了显示器。

然后,我们修改了文件描述符 1 指向的文件到 test 文件。接着两个 echo 命令的输出会自然去找文件描述符 1,然后它看到文件描述符 1 指向的是 test 文件,所以它会把输出写到 test 文件中。

最后,我们用 & 来找到文件描述符 3 指向的目标文件,也就是显示器,然后我们修改了文件描述符1指向的文件到显示器。因此,最后一个 echo 命令会自然的找文件描述符 1 然后输出到显示器上。